﻿// ———————————————————————–
// <copyright file="KMLPlacemark.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// "KMLPlacemark".cs - 
// Project: "ICNETSOA"- "ICNETDAL"
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Don McGarry The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using CoT_Library.Details;
using EDXLCoT;
using EDXLSharp.CAPLib;
using EDXLSharp.EDXLDELib;
using EDXLSharp.EDXLHAVE_2_0Lib;
using EDXLSharp.NIEMCADLib;
using EDXLSharp.NIEMCore;
using GeoOASISWhereLib;
using ICNETServices;
using MEXLSitRepLib;

namespace GISUtil.KML
{
  /// <summary>
  /// KML Placemark
  /// </summary>
  [XmlRoot("Placemark")]
  public class KMLPlacemark
  {
    /// <summary>
    /// Atom Namespace
    /// </summary>
    private const string AtomNamespace = "http://www.w3.org/2005/Atom";

    [XmlAttribute("Id")]
    public string Id { get; set; }

    [XmlElement("name")]
    public string Name { get; set; }

    [XmlElement("description")]
    public string Descrption { get; set; }

    [XmlElement("Style")]
    public KMLStyle Style { get; set; }

    [XmlElement(ElementName = "author", Namespace = "http://www.w3.org/2005/Atom")]
    public KMLAuthor Author { get; set; }

    [XmlIgnore]
    public string AuthorName
    {
      get
      {
        if (Author == null)
        {
          return null;
        }
        return Author.Name;
      }
      set
      {
        if (value == null)
        {
          Author = null;
        }
        else
        {
          if (Author == null)
          {
            Author = new KMLAuthor();
          }
          Author.Name = value;
        }
      }
    }

    [XmlElement(ElementName = "link", Namespace = "http://www.w3.org/2005/Atom")]
    public KMLLink Link { get; set; }

    [XmlIgnore]
    public string LinkUrl
    {
      get
      {
        if (Link == null)
        {
          return null;
        }
        return Link.Url;
      }
      set
      {
        if (value == null)
        {
          Link = null;
        }
        else
        {
          if (Link == null)
          {
            Link = new KMLLink();
          }
          Link.Url = value;
        }
      }
    }

    [XmlElement("TimeSpan")]
    public KMLTimespan Timespan { get; set; }

    [XmlElement("Snippet")]
    public string Snippet { get; set; }

    [XmlElement("Point")]
    public KMLPoint Point { get; set; }
    
    [XmlArray("ExtendedData")]
    public List<KMLExtendedDatum> ExtendedData { get; set; }

    [XmlIgnore]
    public bool ExtendedDataSpecified
    {
      get
      {
        return ExtendedData != null && ExtendedData.Any();
      }
    }

    public KMLPlacemark() { }

    private KMLPlacemark(string id, string author)
    {
      Id = id;
      AuthorName = author;
      Uri uri = new Uri(ICNETSOAConstants.ICNETdatalinkToHTML + id);
      LinkUrl = uri.AbsoluteUri;
      ExtendedData = new List<KMLExtendedDatum>();
    }

    /// <summary>
    /// Creates a KMLPlacemark instance from a CotWrapper object
    /// </summary>
    /// <param name="Id">The ID to use for the placemark</param>
    /// <param name="author">The author</param>
    /// <param name="cot">The CoT object</param>
    internal KMLPlacemark(string id, string author, CoTWrapper cot)
      : this(id, author)
    {
      //Determine the name (fvcot value, icnet value, or uid)
      ICotDetailComponent detailsUID = cot.CoTEvent.Detail.GetFirstElement("uid");
      if (detailsUID != null && detailsUID.XmlNode != null)
      {
        XmlAttributeCollection xac = detailsUID.XmlNode.Attributes;
        XmlAttribute nameAttr = xac["fvcot"] ?? xac["icnet"];
        if (nameAttr != null)
        {
          Name = nameAttr.Value;
        }
      }
      if(string.IsNullOrEmpty(Name))
      {
        Name = cot.CoTEvent.Uid;
      }

      Style = KMLStyleEngine.GetStyle(cot);
      Timespan = new KMLTimespan(cot.CoTEvent.Start, cot.CoTEvent.Stale);
      Snippet = Style.FriendlyName;
      Point = new KMLPoint(cot.CoTEvent.Point.Latitude, cot.CoTEvent.Point.Longitude);
      ParseCotExtendedData(cot);
    }

    /// <summary>
    /// Creates a new instance of a KMLPlacemark from a CAP message
    /// </summary>
    /// <param name="Id">Placemark ID</param>
    /// <param name="author">The author</param>
    /// <param name="cap">CAP message to represent as KML</param>
    internal KMLPlacemark(string id, string author, CAP cap)
      : this(id, author)
    {
      //TODO: KMLPlacemark from CAP
    }

    /// <summary>
    /// Creates a new instance of a KMLPlacemark from a CAD message
    /// </summary>
    /// <param name="Id">Placemark ID</param>
    /// <param name="author">The author</param>
    /// <param name="cad">CAD message to represent as KML</param>
    internal KMLPlacemark(string id, string author, CADEvent cad)
      : this(id, author)
    {
      Name = cad.EventTypeDescriptor.TypeCode.Code.GetText();
      Style = KMLStyleEngine.GetStyle(cad);
      Timespan = new KMLTimespan(cad.ValidityRange.StartDate, cad.ValidityRange.EndDate);
      Snippet = Style.FriendlyName;
      LocationCylinder lc = cad.EventLocation.LocationGeospatialCoordinate as LocationCylinder;
      Point = new KMLPoint(lc.LocationPoint.Point as GMLPoint);
    }

    /// <summary>
    /// Creates a new instance of a KMLPlacemark from a SitRep message
    /// </summary>
    /// <param name="Id">Placemark ID</param>
    /// <param name="author">The author</param>
    /// <param name="sitrep">SitRep message to represent as KML</param>
    internal KMLPlacemark(string id, string author, SitRep sitrep)
      : this(id, author)
    {
      switch (sitrep.MessageReportType)
      {
        case SitRepReportType.FieldObservation:
          SituationObservation sitob = sitrep.Report as SituationObservation;
          if (sitob != null && sitob.ObservationType.HasValue)
          {
            Name = "Field Observation - " + sitob.ObservationType.ToString();
            break;
          }
          goto default;
        case SitRepReportType.CasualtyandIllnessSummary:
        case SitRepReportType.ManagementReportingSummary:
        case SitRepReportType.ResponseResourcesTotals:
        case SitRepReportType.SituationInformation:
        default:
          Name = "Field Observation";
          break;
      }

      Style = KMLStyleEngine.GetStyle(sitrep);
      Timespan = new KMLTimespan(sitrep.FromDateTime, sitrep.ToDateTime);
      Snippet = "Sent by: " + author;
      Point = new KMLPoint(sitrep.ReportLocation.Location as GMLPoint);
      ParseSitRepExtendedData(sitrep);
    }

    /// <summary>
    /// Creates a new instance of a KMLPlacemark from a HAVE message
    /// </summary>
    /// <param name="Id">Placemark ID</param>
    /// <param name="author">The author</param>
    /// <param name="have">HAVE message to represent as KML</param>
    internal KMLPlacemark(string id, string author, EDXLHAVE have)
      : this(id, author)
    {
      HospitalType hospital = have.Hospital[0];
      Name = hospital.Organization.OrganizationInformation.OrgName.NameElement;
      Style = KMLStyleEngine.GetStyle(have);
      Timespan = new KMLTimespan(hospital.LastUpdateTime, hospital.LastUpdateTime.AddDays(1));
      Snippet = "Sent by:" + author;
      Point = new KMLPoint(hospital.Organization.OrganizationGeoLocation.Location as GMLPoint);
      ParseHospitalExtendedData(hospital);
    }

    /// <summary>
    /// Creates a new instance of a KMLPlacemark for non-XML content
    /// </summary>
    /// <param name="Id">Placemark ID</param>
    /// <param name="author">The author</param>
    /// <param name="content">The ContentObject with non-XML content</param>
    /// <param name="deSentTime">The sent DateTime of the DE wrapper</param>
    /// <param name="dePoint">The location indicated by the DE wrapper</param>
    internal KMLPlacemark(string id, string author, ContentObject content, DateTime deSentTime, KMLPoint dePoint)
      : this(id, author)
    {
      Name = content.ContentDescription;
      Descrption = @"<![CDATA[<img alt=""Embedded Image"" src=""" + LinkUrl + @"""/><br/><br/><a href=""" + LinkUrl + @""">Details</a><br/>]]>";
      Style = KMLStyleEngine.GetStyle(content);
      Timespan = new KMLTimespan(deSentTime, deSentTime.AddDays(1));
      Point = dePoint;
    }

    #region ExtendedDataParsing
    /// <summary>
    /// Parses a CoT message to populate ExtendedData
    /// </summary>
    /// <param name="cot"></param>
    private void ParseCotExtendedData(CoTWrapper cot)
    {
      ICotDetailComponent icDetails = cot.CoTEvent.Detail.GetFirstElement("__icnet");
      if (icDetails != null && icDetails.XmlNode != null)
      {
        XmlNode beacon = icDetails.XmlNode.SelectSingleNode("beacon");
        XmlNode icall = icDetails.XmlNode.SelectSingleNode("icall");

        if (beacon != null)
        {
          XmlNode number = beacon.Attributes["number"];
          XmlNode battery = beacon.Attributes["battery"];
          XmlNode signal = beacon.Attributes["signal"];

          if (number != null && !string.IsNullOrEmpty(number.Value))
          {
            string displayName = @"<![CDATA[<b>Phone Number: </b>]]>";
            ExtendedData.Add(new KMLExtendedDatum("number", FormatPhoneNumber(number.Value), displayName));
          }

          double batVal;
          if (battery != null && double.TryParse(battery.Value, out batVal))
          {
            string displayName = @"<![CDATA[<b>Battery Life: </b>]]>";
            string batText = (batVal * 100.0).ToString("##.#") + " %";
            ExtendedData.Add(new KMLExtendedDatum("battery", batText, displayName));
          }

          if (signal != null && !string.IsNullOrWhiteSpace(signal.Value))
          {
            string displayName = @"<![CDATA[<b>Signal Strength: </b>]]>";
            ExtendedData.Add(new KMLExtendedDatum("signal", signal.Value + " dBm", displayName));
          }
        }

        if (icall != null)
        {
          bool editable;
          Boolean.TryParse(icall.Value, out editable);
          ExtendedData.Add(new KMLExtendedDatum("editable", editable.ToString()));
        }
      }
    }

    /// <summary>
    /// Formats a phone number for readability
    /// </summary>
    /// <remarks>Supports 7, 10, and 11 digit numbers</remarks>
    /// <param name="phoneNumber">Number to format</param>
    /// <returns>Formatted number</returns>
    private string FormatPhoneNumber(string number)
    {
      switch (number.Length)
      {
        case 7:
          return number.Substring(0, 3) + "-" + number.Substring(3);
        case 10:
          return number.Substring(0, 3) + "-" + number.Substring(3, 3) + "-"+ number.Substring(6);
        case 11:
          return number.Substring(0, 1) + "-" + number.Substring(1, 3) + "-" + number.Substring(4, 3) + "-" + number.Substring(7);
        default:
          return number;
      }
    }

    /// <summary>
    /// Parses the contents of a HAVE Hospital to populate ExtendedData
    /// </summary>
    /// <param name="hospital"></param>
    private void ParseHospitalExtendedData(HospitalType hospital)
    {
      string displayName = @"<![CDATA[<A href=""" + LinkUrl + @""">Details</a>]]>";
      string value = LinkUrl;
      ExtendedData.Add(new KMLExtendedDatum("details", value, displayName));

      EmergencyDepartmentStatusType erStat = hospital.EmergencyDepartmentStatus;
      ServiceCoverageStatusType scStat = hospital.ServiceCoverageStatus;

      if (erStat.EMSAmbulanceStatus.EMSOffloadStatus.HasValue)
      {
        displayName = @"<![CDATA[<b>EMS Offload: </b>]]>";
        value = erStat.EMSAmbulanceStatus.EMSOffloadStatus.Value.ToString();
        ExtendedData.Add(new KMLExtendedDatum("offload", value, displayName));
      }

      if (erStat.EMSTraffic.EMSTrafficStatus.HasValue)
      {
        displayName = @"<![CDATA[<b>EMS Traffic: </b>]]>";
        value = erStat.EMSTraffic.EMSTrafficStatus.Value.ToString();
        ExtendedData.Add(new KMLExtendedDatum("traffic", value, displayName));
      }

      foreach (var status in erStat.AdditionalStatus)
      {
        displayName = @"<![CDATA[<b>" + status.Key + ": </b>]]>";
        ExtendedData.Add(new KMLExtendedDatum(status.Key, status.Value.ToString(), displayName));
      }

      if (scStat.EmergencyDepartment.HasValue)
      {
        displayName = @"<![CDATA[<b>Emergency Room: </b>]]>";
        ExtendedData.Add(new KMLExtendedDatum("er", scStat.EmergencyDepartment.Value.ToString(), displayName));
      }

      foreach (var status in scStat.AdditionalStatus)
      {
        displayName = @"<![CDATA[<b>" + status.Key + ": </b>]]>";
        ExtendedData.Add(new KMLExtendedDatum(status.Key, status.Value.ToString(), displayName));
      }
    }

    /// <summary>
    /// Parses a SitRep message to populate ExtendedData
    /// </summary>
    /// <param name="sitrep"></param>
    private void ParseSitRepExtendedData(SitRep sitrep)
    {
      string dispName = @"<!CDATA[<a href=""" + LinkUrl + @""">Details</a>]]>";
      ExtendedData.Add(new KMLExtendedDatum("details", LinkUrl, dispName));

      dispName = @"<![CDATA[<b>Observation: </b>]]>";
      string reportText;
      switch (sitrep.MessageReportType)
      {
        case SitRepReportType.FieldObservation:
          SituationObservation sitob = sitrep.Report as SituationObservation;
          if (sitob != null && sitob.ObservationType.HasValue)
          {
            reportText = sitob.ObservationText;
            break;
          }
          goto default;
        case SitRepReportType.CasualtyandIllnessSummary:
        case SitRepReportType.ManagementReportingSummary:
        case SitRepReportType.ResponseResourcesTotals:
        case SitRepReportType.SituationInformation:
        default:
          reportText = "Not Available";
          break;
      }

      ExtendedData.Add(new KMLExtendedDatum("observation", reportText, dispName));
    }

    /// <summary>
    /// Parses a CAD message to populate ExtendedData
    /// </summary>
    /// <param name="cad"></param>
    private void ParseCADExtendedData(CADEvent cad)
    {
      //TODO: CAD Extended Data
    }

    #endregion ExtendedDataParsing
  }
}